package session

import (
	"dmsv/call"
	"dmsv/common"
	"fmt"
	"github.com/beego/beego/v2/core/logs"
	"sync"
	"time"
)

var (
	gEvtCallBack     string
	gListenNumbers   sync.Map
	gServiceCallChan chan *call.CallInfo
	gSessionEvtChan  chan *call.CallEvent
	gSessions        sync.Map
	gSessionRemove   chan *SessionInfo
)

func EnableDirectCall(eventChan chan *call.CallInfo) {
	gServiceCallChan = eventChan
}

func removceRoutine() {

	logs.Debug("启动会话回收任务....")
	for {
		ssToRemove := <-gSessionRemove

		for len(ssToRemove.evChan) > 0 {
			<-ssToRemove.evChan
		}

		for _, c := range ssToRemove.call {
			if !c.IsEnd() {

				logs.Error("[%v]%v 通话仍未结束。", ssToRemove.sessionId, c.CallId())

				if time.Since(ssToRemove.destroyTime) < 4*time.Minute {
					time.AfterFunc(30*time.Second, func() {
						gSessionRemove <- ssToRemove
					})
				}

				continue
			}
		}

		if ssToRemove.evChan != nil {
			close(ssToRemove.evChan)
			ssToRemove.evChan = nil
		}

		gSessions.Delete(ssToRemove.sessionId)
		logs.Debug("[%v] 销毁会话...", ssToRemove.sessionId)
	}
}

func SessionInit(localUrl, fsHost, passwd string) error {

	gEvtCallBack = localUrl
	gSessionEvtChan = make(chan *call.CallEvent, 1000)
	go msgRoutine(gSessionEvtChan)

	gSessionRemove = make(chan *SessionInfo, 1000)
	go removceRoutine()

	return call.Init(fsHost, passwd, gSessionEvtChan)

}

func newCallInProcess(callin *call.CallInfo) {

	cb := &CallBackData{
		CallId: callin.CallId(),
		Callee: callin.Callee,
		Caller: callin.Caller,
		Event:  CALL_EVENT_START,
		Data:   callin,
	}

	if callin.IsUser() {

		cb.Dir = LEG_DIR_USER
		//用户侧呼入,需要根据主叫号查询企业 发送到企业模块
		v, ok := gListenNumbers.Load(callin.Callee)
		if !ok {
			callin.ErrHangUp(nil)

			logs.Error("[%v]new call caller:%v, 中继号:%s 未绑定,drop",
				callin.CallId(), callin.Caller, callin.Callee)

			return
		}

		msgChan, ok := v.(chan *CallBackData)
		if !ok {
			logs.Error("[%v]new call caller:%v, 中继号:%s 绑定错误,drop",
				callin.CallId(), callin.Caller, callin.Callee)
			return
		}

		msgChan <- cb

	} else {
		//发送到外呼模块
		if gServiceCallChan != nil && len(gServiceCallChan) < 1000 {
			gServiceCallChan <- callin
		} else {
			logs.Error("service:%v callout:%v,消息队列已满, drop", callin.Caller, callin.Callee)
		}
	}
}

func msgRoutine(evtChan chan *call.CallEvent) {

	for {

		ev := <-evtChan
		switch ev.Name {

		case call.C_EVENT_CREATE:

			logs.Debug("新的呼入:%v", ev)
			//新的呼入
			newCallInProcess(ev.Call)

		case call.C_EVENT_DESTROY:
			logs.Debug("[%v] 呼叫结束...", ev.Call.CallId())
		}
	}

}

func createSession(appId, mainCallId string, newCall *call.CallInfo, callType CallMethod,
	cb chan *CallBackData) (ss *SessionInfo) {

	sessionId := fmt.Sprintf("%s-%s", mainCallId, callType)
	v, ok := gSessions.Load(sessionId)
	if !ok {
		ss = &SessionInfo{
			caller:       newCall.Caller,
			callee:       newCall.Callee,
			startTime:    newCall.StartTime,
			sessionId:    sessionId,
			callType:     callType,
			appId:        appId,
			CbChan:       cb,
			satisfaction: "-1",
			serviceId:    newCall.ServiceId,
			evChan:       make(chan interface{}, 100),
			call:         make(map[string]*call.CallInfo),
		}

		ss.StoreCalls(newCall)
		gSessions.Store(sessionId, ss)

	} else {
		ss = v.(*SessionInfo)
	}
	return ss
}

//直拨
func DirectCall(appId, callId, display, callee string, c *call.CallInfo, cb chan *CallBackData) *SessionInfo {

	if c.IsEnd() {
		logs.Debug("%v call is hangup,transfer cancel.", c.CallId())
		return nil
	}

	//创建session
	newSs := createSession(appId, callId, c, CALL_TYPE_DIRECT, cb)
	go newSs.eventRoutine()

	gSessions.Store(callId, newSs)

	c.BindSession(callId, newSs.evChan)
	c.BridgeCallToUser(display, callee)

	return newSs
}

//session的事件处理
func (s *SessionInfo) eventRoutine() {

	logs.Info("[%v] 启动session事件处理协程...", s.sessionId)
	for {
		e, ok := <-s.evChan
		if !ok {
			logs.Info("[%v] session is finished...", s.sessionId)
			gSessions.Delete(s.sessionId)
			break
		}

		switch e.(type) {
		case call.CallEvent:
			s.eventRun(e.(call.CallEvent))
		case cmd_info:
			s.cmdRun(e.(cmd_info))
		default:
			logs.Error("错误的事件类型:%v", e)
		}
	}

	logs.Info("[%v] 销毁，session 事件任务退出...", s.sessionId)
}

//用户挂机
func (s *SessionInfo) doDestroy(call *call.CallInfo) {

	//用户和坐席建立通话,且用户先挂机
	if call.HangUpReason != nil {
		switch call.HangUpReason.(type) {
		case common.HangStatus:
			reason := call.HangUpReason.(common.HangStatus)
			switch reason {
			case common.HANG_UP_LINEERR:
				s.hangUpReason = common.ErrUserLineErr
			case common.HANG_UP_REFUSE:
				s.hangUpReason = common.ErrUserRefuse
			case common.HANG_UP_NOANSWER:
				s.hangUpReason = common.ErrUnAnswer
			}
		case common.StatusCode:
			s.hangUpReason = call.HangUpReason
		}
	} else if s.hangCount == 0 && len(s.call) > 1 {
		if !call.AnswerTime.IsZero() {
			s.hangUpReason = common.SuccUserHang
		} else {
			s.hangUpReason = common.ErrCancelByCaller
		}
	}

	s.hangCount++
	logs.Debug("[%v] %v do hangup,call count:%v/%v,reason:%v,callType:%v.",
		s.sessionId, call.CallId(), s.hangCount, len(s.call), s.hangUpReason, s.callType)

	data := &HangInfo{
		RingTime:   call.RingTime,
		AnswerTime: call.AnswerTime,
		HangUpTime: call.EndTime,
		SipCode:    call.HangupSipCode,
		Result:     call.HangupDescribtion,
		Record:     s.record,
		ServiceId:  call.ServiceId,
		Duration:   call.CallTime,
		QueId:      s.queId,
		Reason:     call.HangUpReason,
	}

	if s.hangUpReason == nil {
		s.hangUpReason = call.HangUpReason
	}

	msg := &CallBackData{
		CallId:    s.sessionId,
		Caller:    call.Caller,
		Callee:    call.Callee,
		MsgTime:   time.Now(),
		Event:     CALL_EVENT_DESTROY,
		Data:      data,
		Ssion:     s,
		CallStart: s.startTime,
	}

	msg.Caller = s.caller
	msg.Callee = s.callee
	data.ServiceId = s.GetServiceId()
	msg.Dir = LEG_DIR_USER

	if len(s.CbChan) < 100 {
		s.CbChan <- msg
	} else {
		logs.Error("[%v] [%d]msg is full ,drop it", call.CallId(), len(s.CbChan))
	}

	if s.hangUpReason == common.SuccUserHang {
		s.destroyDo(common.HANG_UP_USER)
	} else {
		s.destroyDo(nil)
	}

	time.AfterFunc(2*time.Minute, func() {
		gSessionRemove <- s
	})
}

//坐席挂机
func (s *SessionInfo) doCallHangup(call *call.CallInfo) {

	if s.hangUpReason == nil {

		switch call.HangUpReason.(type) {
		case common.HangStatus:
			reason := call.HangUpReason.(common.HangStatus)
			switch reason {
			case common.HANG_UP_LINEERR:
				s.hangUpReason = common.ErrSipNetWork
			case common.HANG_UP_REFUSE:
				s.hangUpReason = common.ErrServiceRefuse
			case common.HANG_UP_NOANSWER:
				s.hangUpReason = common.ErrCalleeUnAnswer
			case common.HANG_UP_TIMEOUT:
				s.hangUpReason = common.ErrServiceNoAnswer
			}
		case common.StatusCode:
			s.hangUpReason = call.HangUpReason
		}

		//用户和坐席建立通话,且坐席先挂机
		if s.hangCount == 0 && len(s.call) > 1 &&
			call.HangUpReason == nil && s.hangUpReason == nil {
			s.hangUpReason = common.SuccServiceHang
		}
	}

	s.hangCount++
	logs.Debug("[%v] %v do hangup,call count:%v/%v,当前指令:%v,reason:%v,callType:%v.",
		s.sessionId, call.CallId(), s.hangCount, len(s.call), s.curCmd, s.hangUpReason, s.callType)

	data := &HangInfo{
		RingTime:   call.RingTime,
		AnswerTime: call.AnswerTime,
		HangUpTime: call.EndTime,
		SipCode:    call.HangupSipCode,
		Result:     call.HangupDescribtion,
		Record:     s.record,
		ServiceId:  call.ServiceId,
		Duration:   call.CallTime,
		Reason:     call.HangUpReason,
		QueId:      s.queId,
	}

	msg := &CallBackData{
		CallId:    s.sessionId,
		Caller:    call.Caller,
		Callee:    call.Callee,
		MsgTime:   time.Now(),
		Event:     CALL_EVENT_HANGUP,
		Data:      data,
		Ssion:     s,
		CallStart: s.startTime,
	}

	if s.callType == CALL_TYPE_IN {
		msg.Caller = s.caller
		msg.Callee = s.callee
		data.ServiceId = s.GetServiceId()
	}

	if len(s.CbChan) < 100 {
		s.CbChan <- msg
	} else {
		logs.Error("[%v] [%d]msg is full ,drop it", call.CallId(), len(s.CbChan))
	}
}
